package cn.harperDog.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author hua
 * @description
 * @date 2022/3/6
 * <p>
 * 实现一个方法
 * public static Object execute(String className, String methodName, Object args[])
 * 实现 “通过类的名字、方法名字、方法参数调调用方法，返回值为该方法的返回值。” 的功能。
 * <p>
 * 支持可以调用参数为基本类型的方法
 */
public class InvokeMethodUltimate {
    public static Object execute(String className, String methodName, Object args[]) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        if (className == null || className == "") {
            throw new RuntimeException("classname is blank");
        }

        if (methodName == null || methodName == "") {
            throw new RuntimeException("methodName is blank");
        }

        Class<?> clazz = Class.forName(className);
        Object obj = clazz.newInstance();

        Class<?>[] paramTypes = null;

        if (args == null) {
            paramTypes = new Class[0];
        } else {
            paramTypes = new Class[args.length];
            //获取传入参数类型
            for (int i = 0; i < args.length; i++) {
                paramTypes[i] = args[i].getClass();
            }
        }

        //获取类定义的方法，根据方法名和参数类型列表匹配方法，基本类型做特殊处理
        for (Method declareM : clazz.getDeclaredMethods()) {
            if (declareM.getName().equals(methodName) && isEqualParamType(declareM.getParameterTypes(), paramTypes)) {
                declareM.setAccessible(true);
                return declareM.invoke(obj, args);
            }
        }
        return null;
    }

    /**
     * 比较参数签名是否匹配
     *
     * @param declareParamTypes
     * @param paramTypes
     * @return
     */
    private static boolean isEqualParamType(Class<?>[] declareParamTypes, Class<?>[] paramTypes) {
        if (declareParamTypes.equals(paramTypes) || isEqualBasicType(declareParamTypes, paramTypes)) {
            return true;
        }
        return false;
    }

    /**
     * 将参数类型列表转基本类型比较
     *
     * @param declareParamTypes 类声明方法的参数类型列表
     * @param paramTypes        入参的参数类型列表
     * @return
     */
    private static boolean isEqualBasicType(Class<?>[] declareParamTypes, Class<?>[] paramTypes) {
        for (int i = 0; i < paramTypes.length; i++) {
            if (!declareParamTypes[i].equals(getBasicType(paramTypes[i]))) {
                return false;
            }
        }
        return true;
    }

    private static Class<?> getBasicType(Class<?> paramType) {
        switch (paramType.toString()) {
            case "class java.lang.Byte":
                return byte.class;
            case "class java.lang.Short":
                return short.class;
            case "class java.lang.Integer":
                return int.class;
            case "class java.lang.Long":
                return long.class;
            case "class java.lang.Float":
                return float.class;
            case "class java.lang.Double":
                return double.class;
            case "class java.lang.Character":
                return char.class;
            case "class java.lang.Boolean":
                return boolean.class;
            default:
                return paramType;

        }
    }

    public static void main(String[] args) throws Exception {
//        System.out.println(execute(A.class.getName(), "add", new Integer[]{1, 2}));
//        System.out.println(execute(A.class.getName(), "raw", null));
        System.out.println(execute(A.class.getName(), "strInt", new Object[]{"abc", 1}));
    }
}
